/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include "esp_log.h"
#include "nvs_flash.h"
/* BLE */
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#include "services/gap/ble_svc_gap.h"
#include "modlog/modlog.h"
#include "esp_central.h"
/* WIFI */
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_err.h"
/* 433M */
#include "cmt2300a_params.h"
#include "cmt2300a_hal.h"
#include "radio.h"
/* nrf ble */
#include "driver/uart.h"
#include "esp_log.h"
#include "app_uart.h"
#include "rom/gpio.h"
#include "driver/gpio.h"

#define UART_TASK_PRIORITY           19

static const char *tag = "WIFI_BLE_433_RSSI_TEST";
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t peer_addr[6];
uint8_t test_mode_rx_flag;
static int ble_rssi;

typedef struct rssi_data{
    int wifi;
    int esp_ble;
    int nrf_ble;
    int rf433;

};
rssi_data rssi_data_story;
// WiFi配置信息
#define WIFI_SSID "PG_2.4G"
#define WIFI_PASS "4709@pgtv"
#define LOCK_NAME "LOCKLY5B003264"



struct ble_hs_adv_fields;
struct ble_gap_conn_desc;
struct ble_hs_cfg;
union ble_store_value;
union ble_store_key;

#define BLECENT_SVC_ALERT_UUID              0x1811
#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID  0x2A47
#define BLECENT_CHR_NEW_ALERT               0x2A46
#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID  0x2A48
#define BLECENT_CHR_UNR_ALERT_STAT_UUID     0x2A45
#define BLECENT_CHR_ALERT_NOT_CTRL_PT       0x2A44


static QueueHandle_t uart0_queue;
static uint8_t CMD_HEAD_test[4] = {0xf6,0xe5,0xd4,0xc3};
static uint8_t const BLE_CMD_HEAD_test[4] = {0xA1,0xB2,0xC3,0xD4};


static void uart_event_task_test(void *pvParameters)
{
    uart_event_t event;
    size_t buffered_size;
    uint8_t* dtmp;
	uint16_t packet_len=0,received_len=0;
	bool big_packet=false;
	uint8_t* dtmp_buf=NULL;
    for(;;) {
        //Waiting for UART event.
        if(xQueueReceive(uart0_queue, (void * )&event, (TickType_t)portMAX_DELAY)) {
            // ESP_LOGI(tag, "uart[%d] event:", EX_UART_NUM);
            switch(event.type) {
                //Event of UART receving data
                /*We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full.*/
                case UART_DATA:
                    ESP_LOGI(tag, "[UART DATA Len]: %d", event.size);
                    static uint16_t received_header_size = 0;
                    static uint8_t received_header_data[4] = {0};
                    static bool received_broken_header = false;
                    dtmp = (uint8_t *)malloc(event.size + 4);
                    if (received_broken_header == true)
                    {
                        if (received_header_size < 4)
                        {
                            memcpy(dtmp, received_header_data, received_header_size);
                            uart_read_bytes(EX_UART_NUM, dtmp + received_header_size, event.size, portMAX_DELAY);
                            received_header_size += event.size;
                            memset(received_header_data, 0, 4);
                            received_broken_header = false;
                        }
                        else
                        {
                            received_header_size = 0;
                            memset(received_header_data, 0, 4);
                            received_broken_header = false;
                        }
                    }
                    else
                    {
                        uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                        if ((event.size < 4) && (!memcmp(dtmp, CMD_HEAD_test, event.size)))
                        {
                            received_header_size = event.size;
                            memcpy(received_header_data, dtmp, event.size);
                            received_broken_header = true;
                            ESP_LOGE(tag, "received_header_size: %d", received_header_size);
                        }
                    }

                    esp_log_buffer_hex(tag, dtmp, event.size);
                    if (received_broken_header == false)
                    {
                        if (!memcmp(dtmp, CMD_HEAD_test, 4))
                        {
                            packet_len = (uint16_t)dtmp[4] + (uint16_t)(dtmp[4 + 1] << 8);
                            ESP_LOGI(tag, "[UART Packet Len]: %d", packet_len);

                            // 获取 nordic 扫描的 rssi
                            int nordic_rssi = (uint32_t)(dtmp[15]<<0) + (uint32_t)(dtmp[15 + 1] << 8) + (uint32_t)(dtmp[15 + 2] << 16) + (uint32_t)(dtmp[15 + 3] << 24);

                            // 获取 nordic 扫描的 设备名
                            uint8_t ble_name[15];
                            memcpy(ble_name,&dtmp[19],14);
                            ble_name[sizeof(ble_name) - 1] = '\0';

                            // 获取 nordic 扫描的 mac地址
                            uint8_t ble_mac[6];
                            memcpy(ble_mac,&dtmp[9],6);

                            rssi_data_story.nrf_ble = nordic_rssi;
                            ESP_LOGI(tag, "nordic_rssi HEX = %x DEC: %d ble_name = %s ble_mac = %02x %02x %02x %02x %02x %02x", nordic_rssi, nordic_rssi,ble_name,ble_mac[0],ble_mac[1],ble_mac[2],ble_mac[3],ble_mac[4],ble_mac[5]);

                            if (packet_len > event.size)
                            {
                                big_packet = true;
                                if (received_header_size > 0)
                                {
                                    received_len = received_header_size;
                                    received_header_size = 0;
                                }
                                else
                                {
                                    received_len = event.size;
                                }
                                if (dtmp_buf == NULL)
                                    dtmp_buf = (uint8_t *)malloc(packet_len + 32);
                                memcpy(dtmp_buf, dtmp, received_len);
                                ESP_LOGE(tag, "big_packet received_len: %d", received_len);
                            }
                            else
                            {
                                // app_uart_process(dtmp, event.size);      // 运行到了这里
                                big_packet = false;
                                if (dtmp_buf)
                                {
                                    free(dtmp_buf);
                                    dtmp_buf = NULL;
                                }
                            }
                        }
                        else
                        {
                            if (big_packet == true)
                            {
                                memcpy(dtmp_buf + received_len, dtmp, event.size);
                                received_len += event.size;
                                ESP_LOGE(tag, "big_packet received_len: %d", received_len);
                                if (packet_len > received_len)
                                {
                                    ESP_LOGE(tag, "packet_len: %d", packet_len);
                                    ESP_LOGE(tag, "received_len: %d", received_len);
                                }
                                else
                                {
                                    // app_uart_process(dtmp_buf, received_len);
                                    big_packet = false;
                                    free(dtmp_buf);
                                    dtmp_buf = NULL;
                                }
                            }
                        }
                    }

                    free(dtmp);
                    dtmp = NULL;
                    break;
                // Event of HW FIFO overflow detected
                case UART_FIFO_OVF:
                    ESP_LOGI(tag, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;
                //Event of UART ring buffer full
                case UART_BUFFER_FULL:
                    ESP_LOGI(tag, "ring buffer full");
                    // If buffer full happened, you should consider encreasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;
                //Event of UART RX break detected
                case UART_BREAK:
                    ESP_LOGI(tag, "uart rx break");  // 21引脚没输出 低电平 会一直出现这个
                    break;
                //Event of UART parity check error
                case UART_PARITY_ERR:
                    ESP_LOGI(tag, "uart parity error");
                    break;
                //Event of UART frame error
                case UART_FRAME_ERR:
                    ESP_LOGI(tag, "uart frame error");
                    break;
                //UART_PATTERN_DET
                case UART_PATTERN_DET:
                {
                    uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                    int pos = uart_pattern_pop_pos(EX_UART_NUM);
                    ESP_LOGI(tag, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
                    if (pos == -1) {
                        // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                        // record the position. We should set a larger queue size.
                        // As an example, we directly flush the rx buffer here.
                        uart_flush_input(EX_UART_NUM);
                    } else {
                    	dtmp = (uint8_t*) malloc(pos);
                        uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                        uint8_t pat[PATTERN_CHR_NUM + 1];
                        memset(pat, 0, sizeof(pat));
                        uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
                        ESP_LOGI(tag, "read data: %s", dtmp);
                        ESP_LOGI(tag, "read pat : %s", pat);
						free(dtmp);
    					dtmp = NULL;
                    }
                }
                    break;				
                //Others
                default:
                    ESP_LOGI(tag, "uart event type: %d", event.type);
                    break;
            }
        }
    }
    vTaskDelete(NULL);
}


void factory_uart_init(void)
{

    gpio_pad_select_gpio(21);
	gpio_set_direction(21, GPIO_MODE_OUTPUT);
	gpio_set_level(21, 0);

    esp_log_level_set(tag, ESP_LOG_INFO);

    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    //Install UART driver, and get the queue.
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
    uart_param_config(EX_UART_NUM, &uart_config);

    //Set UART log level
    esp_log_level_set(tag, ESP_LOG_INFO);
    //Set UART pins (using UART0 default pins ie no changes.)
    //uart_set_pin(EX_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
	uart_set_pin(EX_UART_NUM, TXD2_PIN, RXD2_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    //Set uart pattern detect function.
    uart_enable_pattern_det_baud_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 9, 0, 0);
    //Reset the pattern queue length to record at most 20 pattern positions.
    uart_pattern_queue_reset(EX_UART_NUM, 20);

    //Create a task to handler UART event from ISR
    xTaskCreate(uart_event_task_test, "uart_event_task_test", 3072-512, NULL, UART_TASK_PRIORITY, NULL);
}




/**
 * Initiates the GAP general discovery procedure.
 */
static void
blecent_scan(void)
{
    uint8_t own_addr_type;
    struct ble_gap_disc_params disc_params;
    int rc;

    /* Figure out address to use while advertising (no privacy for now) */
    rc = ble_hs_id_infer_auto(0, &own_addr_type);
    if (rc != 0)
    {
        MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
        return;
    }

    /* Tell the controller to filter duplicates; we don't want to process
     * repeated advertisements from the same device.
     */
    disc_params.filter_duplicates = 0;

    /**
     * Perform a passive scan.  I.e., don't send follow-up scan requests to
     * each advertiser.
     */
    disc_params.passive = 0;

    /* Use defaults for the rest of the parameters. */
    disc_params.itvl = 0;
    disc_params.window = 0;
    disc_params.filter_policy = 0;
    disc_params.limited = 0;

    rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
                      blecent_gap_event, NULL);
    if (rc != 0)
    {
        MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
                    rc);
    }
}

/**
 * The nimble host executes this callback when a GAP event occurs.  The
 * application associates a GAP event callback with each connection that is
 * established.  blecent uses the same callback for all connections.
 *
 * @param event                 The event being signalled.
 * @param arg                   Application-specified argument; unused by
 *                                  blecent.
 *
 * @return                      0 if the application successfully handled the
 *                                  event; nonzero on failure.  The semantics
 *                                  of the return code is specific to the
 *                                  particular GAP event being signalled.
 */
static int
blecent_gap_event(struct ble_gap_event *event, void *arg)
{
    struct ble_gap_conn_desc desc;
    struct ble_hs_adv_fields fields;
#if MYNEWT_VAL(BLE_HCI_VS)
#if MYNEWT_VAL(BLE_POWER_CONTROL)
    struct ble_gap_set_auto_pcl_params params;
#endif
#endif
    int rc;
    uint8_t test_mac1[6] = {0x25, 0xed, 0xe2, 0x36, 0xce, 0xf4};
    char ble_name[15];
    switch (event->type)
    {
    case BLE_GAP_EVENT_DISC:

        // 匹配 锁的蓝牙广播名
        if (event->disc.length_data == 16)
        {
            // printf("ble adv data:\r\n");
            for (int i = 0; i < 14; i++)
            {
                // printf("%02x ", event->disc.data[2 + i]);
                ble_name[i] = event->disc.data[2 + i];
            }
            ble_name[14] = '\0';
            // printf("\r\n");
            // printf("scan the lock ble signal the name = %s\r\n", ble_name);

            if(!strcmp(ble_name,LOCK_NAME))
            {   
                ble_rssi = event->disc.rssi;
                // ESP_LOGI(tag, "scan the lock ble signal the name = %s and the rssi = %d\r\n", ble_name,event->disc.rssi);
            }
        }


#if 0       // 单纯匹配mac地址
        if (!memcmp(test_mac1, event->disc.addr.val, 6))
        {
            ble_rssi = event->disc.rssi;
            ESP_LOGI(tag, "scan the lock ble signal the rssi = %d adv_data_LEN = %d\n", event->disc.rssi,event->disc.length_data);
            if (event->disc.length_data == 16)
            {   
                printf("ble adv data:\r\n");
                for (int i = 0; i < 14; i++)
                {
                    printf("%02x ",event->disc.data[2 + i]);
                    ble_name[i] = event->disc.data[2 + i];
                }
                ble_name[14] = '\0';
                printf("\r\n");
                printf("scan the lock ble signal the name = %s\r\n", ble_name);
            }
        }
#endif
        
        
        rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
                                     event->disc.length_data);
        if (rc != 0)
        {
            return 0;
        }

    default:
        return 0;
    }
}

static void
blecent_on_sync(void)
{
    int rc;

    /* Make sure we have proper identity address set (public preferred) */
    rc = ble_hs_util_ensure_addr(0);
    assert(rc == 0);

#if !CONFIG_EXAMPLE_INIT_DEINIT_LOOP
    /* Begin scanning for a peripheral to connect to. */
    blecent_scan();
#endif
}

void blecent_host_task(void *param)
{
    ESP_LOGI(tag, "BLE Host Task Started");
    /* This function will return only when nimble_port_stop() is executed */
    nimble_port_run();

    nimble_port_freertos_deinit();
}

void factory_ble_scan(void)
{
    int rc;
    /* Initialize NVS — it is used to store PHY calibration data */
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ret = nimble_port_init();
    if (ret != ESP_OK)
    {
        ESP_LOGE(tag, "Failed to init nimble %d ", ret);
        return;
    }

    /* Configure the host. */
    ble_hs_cfg.sync_cb = blecent_on_sync;

    nimble_port_freertos_init(blecent_host_task);

#if CONFIG_EXAMPLE_INIT_DEINIT_LOOP
    stack_init_deinit();
#endif
}


// 事件处理回调函数
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        esp_wifi_connect();
        ESP_LOGI(tag, "重新连接WiFi...");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(tag, "成功获取IP: " IPSTR, IP2STR(&event->ip_info.ip));
    }
}

void factory_wifi_init_sta(void)
{
    // 初始化NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // 初始化WiFi
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件处理器
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    // 配置WiFi
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            .threshold = {
                .authmode = WIFI_AUTH_WPA2_PSK // 正确地初始化 authmode
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(tag, "WiFi初始化完成，正在连接...");
}



void wifi_ble_test_task(void)
{

    wifi_ap_record_t ap_info;
    static int rf_433M_rssi;
    static uint32_t time_cunt;

    ESP_LOGI(tag,"RF_rssi_test INIT");

    RF_Init();
    factory_wifi_init_sta();
    factory_ble_scan();
    factory_uart_init();
    //配置红灯为输入输出模式
    gpio_set_direction(GPIO_NUM_18, GPIO_MODE_INPUT_OUTPUT);

    while (1)
    {
     
        time_cunt++;
        // 获取当前连接WiFi的AP信息
        if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK)
        {
            rssi_data_story.wifi = ap_info.rssi;
            // ESP_LOGI(tag, "current wifi RSSI: %d dBm", rssi_data_story.wifi);
        }
        else
        {
            //wifi还没连接
            ESP_LOGE(tag, "wifi connect error");
        }

        // 获取当前扫描到 ble的rssi信息
        if (ble_rssi == 0)
        {
            // 此时蓝牙还没扫描的对应的广播
            ESP_LOGI(tag, "ble scan error");
        }
        else
        {
            rssi_data_story.esp_ble = ble_rssi;
            // ESP_LOGI(tag, "current ble  RSSI: %d dBm", rssi_data_story.ble);
        }

        // 获取当前 433M模块检测到 的rssi信息
        rf_433M_rssi = CMT2300A_GetRssiDBm();
        // ESP_LOGI("runing", "rssi = %d ", rf_433M_rssi);
        // 过滤掉 小于 -85 的信号，
        // 在触发了 433模块 接收 中断后 获取当前的rssi的值。
        if ((rf_433M_rssi > -85) && (test_mode_rx_flag) )
        {
            rssi_data_story.rf433 = rf_433M_rssi;
            // ESP_LOGW("RF_rssi_test", "rssi = %d  ", rssi_data_story.rf433);
            test_mode_rx_flag = false;
        }

        // 1秒钟打印一次rssi的数值
        if ((time_cunt % 500 == 0))
        {
            // 这里发送的想要扫描的设备名，mac地址 ，扫描时间，循环次数 
            uint8_t send_data[25] = {0xf6, 0xe5, 0xd4, 0xc3, 0x19, 0x00, 0x01, 0x10, 0x27, 0x01, 0x0e, 0x4c, 0x4f, 0x43, 0x4b, 0x4c, 0x59, 0x35, 0x42, 0x30 ,0x30, 0x33, 0x32, 0x36, 0x34 };
            int err = uart_write_bytes(EX_UART_NUM, send_data, sizeof(send_data));

            ESP_LOGI(tag, "current wifi     RSSI: %d dBm", rssi_data_story.wifi);
            ESP_LOGI(tag, "current esp_ble  RSSI: %d dBm", rssi_data_story.esp_ble);
            ESP_LOGI(tag, "current nrf_ble  RSSI: %d dBm", rssi_data_story.nrf_ble);
            ESP_LOGW(tag, "current 433M     RSSI: %d dbm", rssi_data_story.rf433);
                        
        }

        //红灯快速闪烁
        int led_state = gpio_get_level(GPIO_NUM_18);  
        gpio_set_level(GPIO_NUM_18, !led_state);      

        // 50ms 获取一次 rssi的数值
        vTaskDelay(pdMS_TO_TICKS(50));
    }
}




void factory_test_wifi_ble_rssi(void)
{
    xTaskCreate(wifi_ble_test_task, "wifi_test_task", 1024 * 10, NULL, 5, NULL);
}